package com.lzw.app;

import com.lzw.data.AppRepository;
import com.lzw.main.ClearThread;
import com.lzw.main.ListenerThread;
import com.lzw.main.PingPongThread;
import com.lzw.main.ShowThread;
import com.lzw.property.LzwProperties;
import com.lzw.property.ServerProperties;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 自定义注册中心服务应用对象
 * @author LZW
 */
public class ServerApp {
    //注册服务的存放容器
    private AppRepository appRepository = new AppRepository();
    //其他注册中心的集群信息
    private AppRepository serverRepository = new AppRepository();
    //注册中心应用的状态：true表示开启
    public static boolean status;
    //此服务端口+ip
    private static String ip_self;
    private static int port_self;

    //任务调度线程池
    private static ThreadPoolExecutor taskThreadPool;

    //注册监听线程池
    private static ThreadPoolExecutor listenerThreadPool;

    //单例模式——懒汉式
    private static ServerApp serverApp;

    //静态代码块，初始化服务资源
    static {
        //任务线程
        initTaskPool();

        //监听线程
        initListenerPool();
    }


    private ServerApp(String ip, int port) {
        ip_self = ip;
        port_self = port;
    }

    /**
     * 初始化监听线程池
     */
    private static void initListenerPool() {
        int initSize = 10;
        int maxSize = 15;
        int aliveSecond = 60;
        listenerThreadPool = new ThreadPoolExecutor(initSize, maxSize, aliveSecond, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
            private Integer id = 0;

            public Thread newThread(Runnable r) {
                synchronized (ServerApp.class) {
                    Thread t = new Thread(Thread.currentThread().getThreadGroup(), r, "监听线程-" + id);
                    id++;
                    if (t.isDaemon()) {
                        t.setDaemon(false);
                    }
                    if (t.getPriority() != Thread.NORM_PRIORITY) {
                        t.setPriority(Thread.NORM_PRIORITY);
                    }
                    return t;
                }
            }
        });
        listenerThreadPool.prestartAllCoreThreads();
    }


    //初始化任务线程池
    private static void initTaskPool() {
        //初始大小
        int initSizeTask = 10;
        //最大容量
        int maxSizeTask = 15;
        //空闲线程的最大存活时间
        int aliveSecondTask = 60;

        //重写线程工程
        taskThreadPool = new ThreadPoolExecutor(initSizeTask, maxSizeTask, aliveSecondTask, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
            //用于分配线程名
            private int id = 0;

            public Thread newThread(Runnable r) {
                synchronized (ServerApp.class) {
                    Thread t = new Thread(Thread.currentThread().getThreadGroup(), r, "任务线程-" + id);
                    id++;
                    if (t.isDaemon()) {
                        t.setDaemon(false);
                    }
                    if (t.getPriority() != Thread.NORM_PRIORITY) {
                        t.setPriority(Thread.NORM_PRIORITY);
                    }
                    return t;
                }
            }
        });
        taskThreadPool.prestartAllCoreThreads();
    }

    public AppRepository getAppRepository() {
        return appRepository;
    }

    public void setAppRepository(AppRepository appRepository) {
        this.appRepository = appRepository;
    }

    public AppRepository getServerRepository() {
        return serverRepository;
    }

    public void setServerRepository(AppRepository serverRepository) {
        this.serverRepository = serverRepository;
    }

    public ThreadPoolExecutor getListenerThreadPool() {
        return listenerThreadPool;
    }

    public void setListenerThreadPool(ThreadPoolExecutor listenerThreadPool) {
        ServerApp.listenerThreadPool = listenerThreadPool;
    }

    public ThreadPoolExecutor getTaskThreadPool() {
        return taskThreadPool;
    }

    public void setTaskThreadPool(ThreadPoolExecutor taskThreadPool) {
        ServerApp.taskThreadPool = taskThreadPool;
    }

    /**
     * 获取实例，并初始化注册中心
     *
     * @param serverProperties 服务配置参数信息
     * @param lzwProperties    注册中心配置信息
     * @return 实例对象
     * @throws IOException
     */
    public static synchronized ServerApp getInstance(LzwProperties lzwProperties, ServerProperties serverProperties) throws IOException {
        String ip = serverProperties.getIp();
        int port = serverProperties.getPort();
        if (serverApp == null) {
            serverApp = new ServerApp(ip, port);
        }
        status = true;
        //开启注册动作监听
        startListener(port, serverApp);
        //开启无效服务剔除
        startClear(serverApp, lzwProperties);
        //开启集群注册监听
        startServer(serverProperties, lzwProperties);
        //开启服务日志展示
        showService(serverApp);
        //返回app对象
        return serverApp;
    }

    /**
     * 展示日志
     *
     * @param serverApp 服务实例对象
     */
    private static void showService(ServerApp serverApp) {
        taskThreadPool.execute(new ShowThread(serverApp));
    }

    /**
     * 注册集群并启动ping-pong心跳续约
     *
     * @param serverProperties 服务配置参数信息
     * @param lzwProperties    注册中心配置信息
     */
    private static synchronized void startServer(ServerProperties serverProperties, LzwProperties lzwProperties) {
        //注册集群
        registerServers(lzwProperties, serverProperties, taskThreadPool);
    }

    /**
     * 执行注册集群
     *
     * @param serverProperties 服务配置参数信息
     * @param lzwProperties    注册中心配置信息
     * @param poolExecutor     线程池
     */
    private static synchronized void registerServers(final LzwProperties lzwProperties, final ServerProperties serverProperties, ThreadPoolExecutor poolExecutor) {
        List<String> serveripPort = lzwProperties.getServerIpPort();
        if (serveripPort == null || serveripPort.size() == 0) {
            return;
        }
        for (final String ipPort : serveripPort) {
            poolExecutor.execute(new Runnable() {
                public void run() {
                    registerServer(ipPort, lzwProperties, serverProperties);
                    //开启ping-pong
                    taskThreadPool.execute(new PingPongThread(lzwProperties, serverProperties, taskThreadPool));
                }
            });
        }
    }

    /**
     * 注册服务
     *
     * @param ipPort          此注册中心ip
     * @param serverProperties 服务配置参数信息
     * @param lzwProperties    注册中心配置信息
     */
    private static synchronized void registerServer(String ipPort, LzwProperties lzwProperties, ServerProperties serverProperties) {
        int invalidateTime = lzwProperties.getInvalidateTime();
        if (ipPort == null || invalidateTime == 0) {
            System.err.println("url或失效时间不正常");
            return;
        }
        while (true) {
            try {
                String[] split = ipPort.split(":");
                String ip = split[0];
                int port = Integer.parseInt(split[1]);
                Socket socket = new Socket(ip, port);
                OutputStream outputStream = socket.getOutputStream();
                InputStream inputStream = socket.getInputStream();
                BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
                BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
                //传递action信息
                bw.write("lzw");
                bw.newLine();
                bw.flush();
                //接收response信息
                String status = br.readLine();
                if ("OK".equals(status)) {
                    ObjectOutputStream oos = new ObjectOutputStream(outputStream);
                    Instance instance = new Instance();
                    instance.setHost(ip_self);
                    instance.setPort(port_self);
                    String appName = lzwProperties.getAppName();
                    instance.setName(appName);
                    instance.setUrl("http://" + instance.getHost() + ":" + instance.getPort());
                    instance.setId(instance.getHost() + ":" + instance.getName() + ":" + instance.getPort());
                    instance.setInvalidateSecond(invalidateTime);
                    oos.writeObject(instance);
                    oos.flush();
                }
                //接收反馈
                String resp = br.readLine();
                socket.close();
                break;
            } catch (Exception e) {
                System.out.println("超时：找不到集群服务器。。集群注册失败！重新尝试。。");
            }
        }
    }

    /**
     * 开启线程进行服务信息清理
     *
     * @param serverApp     此服务实例对象
     * @param lzwProperties 注册中心配置信息
     */
    private static synchronized void startClear(ServerApp serverApp, LzwProperties lzwProperties) {
        int interval = lzwProperties.getClearInterval();
        taskThreadPool.execute(new ClearThread(serverApp, interval));
    }

    /**
     * 开启注册监听线程
     *
     * @param port      监听端口
     * @param serverApp 此应用实例
     * @throws IOException 可能抛出异常
     */
    private static synchronized void startListener(int port, ServerApp serverApp) throws IOException {
        //使线程池中所有线程对该端口进行监听，一但一个socket正在监听，其他监听会被阻塞？
        int maxCount = listenerThreadPool.getPoolSize();
        ServerSocket serverSocket = new ServerSocket(port);

        synchronized (ServerApp.class) {
            int activeCount = listenerThreadPool.getActiveCount();
            while (maxCount > activeCount) {
                listenerThreadPool.execute(new ListenerThread(serverSocket, serverApp));
                activeCount = listenerThreadPool.getActiveCount();
            }

        }
    }
}
